package com.unc.allergyex.helpers;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;

import android.content.Context;
/**
 * This class handles the mechanics of fetching requests from the server but does not handle deserialization, 
 * which is handled by {@link Deserializer} or directly by Gson wherever deserialization is needed.
 * The url, username, and password in the config file used here must never be checked into source control as they are used to access the server.
 * @author Ryan Miller
 *
 * @see com.google.gson.Gson
 */
public class Service {
	
	private String PROTOCOL;
	private String HOST_NAME;
	private String USERNAME;
	private String PASSWORD;
	private String VERSIONS_PATH;
	private String ALLERGENS_PATH;
	private String RESTAURANTS_PATH;
	private String MENUS_SUBPATH;
	
	private Context context;
	private String error;
	
	/**
	 * Constructs a new instance of the service to be used to make HTTP requests to the server.
	 * Used by {@link Disambiguation} and {@link AllergyEX} to query for restaurant menus 
	 * and by {@link Updater} to update locally stored versioned objects from the server.
	 * 
	 * @param newContext the Context of the constructing Activity
	 */
	
	public Service(Context newContext){
		context = newContext;
		try {
			loadCredentials();
		} catch (Exception e){
			this.error = "Failed to load server information from config file.";
		}
		this.error = "";
	}
	
	/**
	 * Returns the error property for this object that is set in the event of any error.
	 * Specifically, an error is only set if the credentials config file fails to load during construction.
	 * 
	 * @return a String that describes the error that occurred; if no error has occurred, returns empty string
	 */
	
	public String getError(){
		return error;
	}
	
	/**
	 * Retrieves version numbers for versioned objects from server as JSON.
	 * 
	 * @return JSON encoded array of the names of versioned objects ("name") and their version numbers ("value")
	 * @throws ClientProtocolException exception thrown by execute() method of HttpClient object
	 * @throws IOException exception thrown by execute() method of HttpClient object
	 */
	public String getVersions() throws ClientProtocolException, IOException{
		return doGet(VERSIONS_PATH);
	}
	
	/**
	 * Retrieves all restaurants from server as JSON.
	 * 
	 * @return JSON encoded array of all supported restaurant objects
	 * @throws ClientProtocolException exception thrown by execute() method of HttpClient object
	 * @throws IOException exception thrown by execute() method of HttpClient object
	 */
	public String getRestaurants() throws ClientProtocolException, IOException{
		return doGet(RESTAURANTS_PATH);
	}
	
	/**
	 * Retrieves all alergen from server as JSON.
	 * 
	 * @return JSON encoded array of all supported allergen objects
	 * @throws ClientProtocolException exception thrown by execute() method of HttpClient object
	 * @throws IOException exception thrown by execute() method of HttpClient object
	 */
	public String getAllergens() throws ClientProtocolException, IOException{
		return doGet(ALLERGENS_PATH);
	}
	
	
	/**
	 * Retrieves menu for restaurant from server as JSON.
	 * 
	 * @return JSON encoded object for restaurant and all of its sub-objects (categories, dishes, components, allergens)
	 * @throws ClientProtocolException exception thrown by execute() method of HttpClient object
	 * @throws IOException exception thrown by execute() method of HttpClient object
	 */
	public String unambiguousIdQuery(String restaurantId) throws ClientProtocolException, IOException{
		return doGet(MENUS_SUBPATH+restaurantId+".json");
	}
	
	/**
	 * Retrieves requested String from server as JSON.
	 * This method is only called privately by other public methods in this class.
	 * 
	 * @return JSON encoded object for restaurant and all of its sub-objects (categories, dishes, components, allergens)
	 * @throws ClientProtocolException exception thrown by execute() method of HttpClient object
	 * @throws IOException exception thrown by execute() method of HttpClient object
	 */
	private String doGet(String path) throws ClientProtocolException, IOException{
	    // Create a new HttpClient and Post Header
	    HttpClient http = new DefaultHttpClient();
	    String url = PROTOCOL+HOST_NAME+path;
	    HttpGet get = new HttpGet(url);
	    
	    //set the username and password
	    ((AbstractHttpClient) http).getCredentialsProvider().setCredentials(
                new AuthScope(HOST_NAME, AuthScope.ANY_PORT),
                new UsernamePasswordCredentials(this.USERNAME, this.PASSWORD)
        );
	    
	    //make the request
	    ResponseHandler<String> responseHandler = new BasicResponseHandler();
	    String response;
		response = http.execute(get, responseHandler);
	    return response;	    
	}
	
	/**
	 * Loads server communication data into private fields of this class.
	 * Specifically, username, password, and url components are loaded from the credentials.config file located in assets.
	 * This config file should never be exposed publicly as it allows access to the server.
	 */
	private void loadCredentials() throws FileNotFoundException, IOException{
		Properties config = new Properties();
		config.load(context.getAssets().open("credentials.config"));
		this.PROTOCOL = config.getProperty("PROTOCOL");
		this.HOST_NAME = config.getProperty("HOST_NAME");
		this.USERNAME = config.getProperty("USERNAME");	
		this.PASSWORD = config.getProperty("PASSWORD");	
		this.VERSIONS_PATH = config.getProperty("VERSIONS_PATH");
		this.ALLERGENS_PATH = config.getProperty("ALLERGENS_PATH");
		this.RESTAURANTS_PATH = config.getProperty("RESTAURANTS_PATH");
		this.MENUS_SUBPATH = config.getProperty("MENUS_SUBPATH");
	}	
}
